package com.randb.easyhttp.request;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.TextUtils;

import com.randb.easyhttp.EasyHttp;
import com.randb.easyhttp.api.ApiService;
import com.randb.easyhttp.cache.RxCache;
import com.randb.easyhttp.cache.coverter.IDiskConverter;
import com.randb.easyhttp.cache.model.CacheMode;
import com.randb.easyhttp.cache.model.CacheResult;
import com.randb.easyhttp.callback.CallbackProxy;
import com.randb.easyhttp.https.HttpsUtils;
import com.randb.easyhttp.interceptor.BaseDynamicInterceptor;
import com.randb.easyhttp.interceptor.CacheInterceptor;
import com.randb.easyhttp.interceptor.HeadersInterceptor;
import com.randb.easyhttp.interceptor.NoCacheInterceptor;
import com.randb.easyhttp.model.ApiResult;
import com.randb.easyhttp.model.HttpHeaders;
import com.randb.easyhttp.model.HttpParams;
import com.randb.easyhttp.utils.HttpLog;
import com.randb.easyhttp.utils.RxUtils;
import com.randb.easyhttp.utils.Utils;

import java.io.File;
import java.io.InputStream;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;

import io.reactivex.Observable;
import io.reactivex.functions.Consumer;
import okhttp3.Cache;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;

/**
 * User: RandBII
 * Date: 2020/5/12
 * Description:
 */
public abstract class BaseRequest<R extends BaseRequest> {

    protected Cache cache;
    protected CacheMode cacheMode = CacheMode.NO_CACHE;
    protected long cacheTime = -1;
    protected String cacheKey;
    protected IDiskConverter diskConverter;
    protected String url;
    protected String baseUrl;
    protected long readeTimeout;
    protected long writeTimeout;
    protected long connectTimeout;
    protected int retryCount;
    protected int retryDelay;
    protected int retryIncreaseDelay;
    protected boolean isSyncRequest;
    protected List<Cookie> cookies = new ArrayList<>();
    protected final List<Interceptor> networkInterceptors = new ArrayList<>();
    protected HttpHeaders headers = new HttpHeaders();
    protected HttpParams params = new HttpParams();
    protected Retrofit retrofit;
    protected RxCache rxCache;
    protected ApiService apiManager;
    protected OkHttpClient okHttpClient;
    protected Context context;
    private boolean sign = false;
    private boolean timeStamp = false;
    private boolean accessToken = false;
    protected HttpUrl httpUrl;
    protected Proxy proxy;
    protected HttpsUtils.SSLParams sslParams;

    protected HostnameVerifier hostnameVerifier;

    protected List<Converter.Factory> converterFactories = new ArrayList<>();
    protected List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    protected final List<Interceptor> interceptors = new ArrayList<>();

    public BaseRequest(String url) {
        this.url = url;
        context = EasyHttp.getContext();
        EasyHttp config = EasyHttp.getInstance();
        this.baseUrl = EasyHttp.getBaseUrl();
        if (!TextUtils.isEmpty(baseUrl)) {
            httpUrl = HttpUrl.parse(url);
        }
        if (baseUrl == null && url != null
                && (url.startsWith("http://") || url.startsWith("https://"))) {
            httpUrl = HttpUrl.parse(url);
            baseUrl = httpUrl.url().getProtocol() + "://" + httpUrl.url().getHost() + "/";
        }

        cacheMode = config.getCacheMode();
        cacheTime = config.getCacheTime();
        retryCount = config.getRetryCount();
        retryDelay = config.getRetryDelay();
        retryIncreaseDelay = config.getRetryIncreaseDelay();
        cache = config.getHttpCache();

        String acceptLanguage = HttpHeaders.getAcceptLanguage();

        if (!TextUtils.isEmpty(acceptLanguage)) {
            headers(HttpHeaders.HEAD_KEY_ACCEPT_LANGUAGE, acceptLanguage);
        }
        String userAgent = HttpHeaders.getUserAgent();
        if (!TextUtils.isEmpty(userAgent)) {
            headers(HttpHeaders.HEAD_KEY_USER_AGENT, userAgent);
        }

        if (config.getCommonParams() != null) {
            params.put(config.getCommonParams());
        }

        if (config.getCommonHeaders() != null) {
            headers.put(config.getCommonHeaders());
        }

    }


    public R headers(HttpHeaders headers) {
        headers.put(headers);
        return (R) this;
    }

    public R headers(String key, String value) {
        headers.put(key, value);
        return (R) this;
    }

    public HttpParams getParams() {
        return this.params;
    }

    public R readTimeOut(long readeTimeout) {
        this.readeTimeout = readeTimeout;
        return (R) this;
    }


    public R writeTimeOut(long writeTimeout) {
        this.writeTimeout = writeTimeout;
        return (R) this;
    }

    public R okCache(Cache cache) {
        this.cache = cache;
        return (R) this;
    }

    public R cacheMode(CacheMode cacheMode) {
        this.cacheMode = cacheMode;
        return (R) this;
    }


    public R cacheKey(String cacheKey) {
        this.cacheKey = cacheKey;
        return (R) this;
    }

    public R cacheTime(long cacheTime) {
        if (cacheTime <= -1) cacheTime = EasyHttp.DEFAULT_CACHE_NEVER_EXPIRE;
        this.cacheTime = cacheTime;
        return (R) this;
    }

    public R baseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        if (!TextUtils.isEmpty(baseUrl)) {
            httpUrl = HttpUrl.parse(baseUrl);
        }
        return (R) this;
    }

    public R retryCount(int retryCount) {
        if (retryCount < 0) throw new IllegalArgumentException("retryCount must > 0");
        this.retryCount = retryCount;
        return (R) this;
    }

    public R retryDelay(int retryDelay) {
        if (retryDelay < 0) throw new IllegalArgumentException("retryDelay must > 0");
        this.retryDelay = retryDelay;
        return (R) this;
    }

    public R retryIncreaseDelay(int retryIncreaseDelay) {
        if (retryIncreaseDelay < 0)
            throw new IllegalArgumentException("retryIncreaseDelay must > 0");
        this.retryIncreaseDelay = retryIncreaseDelay;
        return (R) this;
    }

    public R addInterceptor(Interceptor interceptor) {
        interceptors.add(Utils.checkNotNull(interceptor, "interceptor == null"));
        return (R) this;
    }

    public R addNetworkInterceptor(Interceptor interceptor) {
        networkInterceptors.add(Utils.checkNotNull(interceptor, "interceptor == null"));
        return (R) this;
    }

    public R addCookie(String name, String value) {
        Cookie.Builder builder = new Cookie.Builder();
        Cookie cookie = builder.name(name).value(value).domain(httpUrl.host()).build();
        this.cookies.add(cookie);
        return (R) this;
    }

    public R addCookie(Cookie cookie) {
        this.cookies.add(cookie);
        return (R) this;
    }

    public R addCookies(List<Cookie> cookies) {
        this.cookies.addAll(cookies);
        return (R) this;
    }

    public R addConverterFactory(Converter.Factory factory) {
        converterFactories.add(factory);
        return (R) this;
    }

    public R addCallAdapterFactory(CallAdapter.Factory factory) {
        adapterFactories.add(factory);
        return (R) this;
    }

    public R okProxy(Proxy proxy) {
        this.proxy = proxy;
        return (R) this;
    }


    public R cacheDiskConverter(IDiskConverter converter) {
        this.diskConverter = Utils.checkNotNull(converter, "conveter ==null");
        return (R) this;
    }

    public R hostnameVerifier(HostnameVerifier verifier) {
        this.hostnameVerifier = verifier;
        return (R) this;
    }

    public R certificates(InputStream... certificates) {
        this.sslParams = HttpsUtils.getSSLSocketFactory(null, null, certificates);
        return (R) this;
    }

    public R certificates(InputStream bksFile, String password, InputStream... certificates) {
        this.sslParams = HttpsUtils.getSSLSocketFactory(bksFile, password, certificates);
        return (R) this;
    }


    public R removeHeaders(String key) {
        headers.remove(key);
        return (R) this;
    }

    public R removeAllHeaders() {
        headers.clear();
        return (R) this;
    }

    public R params(HttpParams params) {
        this.params.put(params);
        return (R) this;
    }

    public R params(String key, String value) {
        params.put(key, value);
        return (R) this;
    }

    public R params(Map<String, String> keyValues) {
        params.put(keyValues);
        return (R) this;
    }

    public R removeParam(String key) {
        params.remove(key);
        return (R) this;
    }

    public R removeAllParams() {
        params.clear();
        return (R) this;
    }

    public R sign(boolean sign) {
        this.sign = sign;
        return (R) this;
    }

    public R timeStamp(boolean timeStamp) {
        this.timeStamp = timeStamp;
        return (R) this;
    }

    public R accessToken(boolean accessToken) {
        this.accessToken = accessToken;
        return (R) this;
    }

    public R syncRequest(boolean syncRequest) {
        this.isSyncRequest = syncRequest;
        return (R) this;
    }

    @SuppressLint("CheckResult")
    public void removeCache(String key) {
        EasyHttp.getRxCache().remove(key)
                .compose(RxUtils.<Boolean>io_main())
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        HttpLog.i("removeCache success!!!");
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        HttpLog.i("removeCache error!!!" + throwable.getMessage());

                    }
                });
    }


    private OkHttpClient.Builder generateOkClient() {

        if (readeTimeout <= 0 && writeTimeout <= 0
                && connectTimeout <= 0 && sslParams == null
                && cookies.size() == 0 && hostnameVerifier == null
                && proxy == null && headers.isEmpty()) {
            OkHttpClient.Builder builder = EasyHttp.getOkHttpClientBuilder();
            for (Interceptor interceptor : builder.interceptors()) {
                if (interceptor instanceof BaseDynamicInterceptor) {
                    ((BaseDynamicInterceptor) interceptor).sign(sign)
                            .timeStamp(timeStamp)
                            .accessToken(accessToken);
                }
            }
            return builder;
        } else {
            final OkHttpClient.Builder newClientBuilder = EasyHttp.getOkHttpClient().newBuilder();
            if (readeTimeout > 0)
                newClientBuilder.readTimeout(readeTimeout, TimeUnit.MILLISECONDS);
            if (writeTimeout > 0)
                newClientBuilder.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS);
            if (connectTimeout > 0)
                newClientBuilder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
            if (hostnameVerifier != null) newClientBuilder.hostnameVerifier(hostnameVerifier);
            if (sslParams != null)
                newClientBuilder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
            if (proxy != null) newClientBuilder.proxy(proxy);
            if (cookies.size() > 0) EasyHttp.getCookieJar().addCookies(cookies);

            //添加头  头添加放在最前面方便其他拦截器可能会用到
            newClientBuilder.addInterceptor(new HeadersInterceptor(headers));

            for (Interceptor interceptor : interceptors) {
                if (interceptor instanceof BaseDynamicInterceptor) {
                    ((BaseDynamicInterceptor) interceptor).sign(sign).timeStamp(timeStamp).accessToken(accessToken);
                }
                newClientBuilder.addInterceptor(interceptor);
            }
            for (Interceptor interceptor : newClientBuilder.interceptors()) {
                if (interceptor instanceof BaseDynamicInterceptor) {
                    ((BaseDynamicInterceptor) interceptor).sign(sign).timeStamp(timeStamp).accessToken(accessToken);
                }
            }
            if (networkInterceptors.size() > 0) {
                for (Interceptor interceptor : networkInterceptors) {
                    newClientBuilder.addNetworkInterceptor(interceptor);
                }
            }
            return newClientBuilder;
        }
    }


    private Retrofit.Builder generateRetrofit() {

        if (converterFactories.isEmpty()
                && adapterFactories.isEmpty()) {
            Retrofit.Builder builder = EasyHttp.getRetrofitBuilder();
            if (!TextUtils.isEmpty(baseUrl)) builder.baseUrl(baseUrl);
            return builder;
        } else {

            Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
            if (!TextUtils.isEmpty(baseUrl)) retrofitBuilder.baseUrl(baseUrl);
            if (!converterFactories.isEmpty()) {
                for (Converter.Factory factory : converterFactories) {
                    retrofitBuilder.addConverterFactory(factory);
                }
            } else {
                Retrofit.Builder newBuilder = EasyHttp.getRetrofitBuilder();
                if (!TextUtils.isEmpty(baseUrl)) newBuilder.baseUrl(baseUrl);
                List<Converter.Factory> myConverters = newBuilder.build().converterFactories();
                for (Converter.Factory factory : myConverters) {
                    retrofitBuilder.addConverterFactory(factory);
                }
            }
            if (!adapterFactories.isEmpty()) {
                for (CallAdapter.Factory factory : adapterFactories) {
                    retrofitBuilder.addCallAdapterFactory(factory);
                }
            } else {
                Retrofit.Builder newBuilder = EasyHttp.getRetrofitBuilder();
                List<CallAdapter.Factory> listAdapterFactories = newBuilder.baseUrl(baseUrl)
                        .build().callAdapterFactories();
                for (CallAdapter.Factory f : listAdapterFactories) {
                    retrofitBuilder.addCallAdapterFactory(f);
                }

            }

            return retrofitBuilder;

        }

    }


    private RxCache.Builder generateRxCache() {

        final RxCache.Builder rxCacheBuilder = EasyHttp.getRxCacheBuilder();
        switch (cacheMode) {
            case NO_CACHE:
                final NoCacheInterceptor NOCACHEINTERCEPTOR = new NoCacheInterceptor();
                interceptors.add(NOCACHEINTERCEPTOR);
                networkInterceptors.add(NOCACHEINTERCEPTOR);
                break;
            case DEFAULT:// okhttp 的缓存
                if (this.cache == null) {
                    File cacheDirectory = EasyHttp.getCacheDirectory();
                    if (cacheDirectory == null) {
                        cacheDirectory = new File(EasyHttp.getContext().getCacheDir(), "okhttp-cache");
                    } else {
                        if (cacheDirectory.isDirectory()
                                && !cacheDirectory.exists()) {
                            cacheDirectory.mkdirs();
                        }
                        this.cache = new Cache(cacheDirectory, Math.max(5 * 1024 * 1024, EasyHttp.getCacheMaxSize()));
                    }
                }
                String cacheControlValue = String.format("max-age=%d", Math.max(-1, cacheTime));
                final CacheInterceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new CacheInterceptor(EasyHttp.getContext(), cacheControlValue);

                break;
            case FIRSTREMOTE:
            case FIRSTCACHE:
            case ONLYREMOTE:
            case ONLYCACHE:
            case CACHEANDREMOTE:
            case CACHEANDREMOTEDISTINCT:
                interceptors.add(new NoCacheInterceptor());
                if (diskConverter == null) {
                    final RxCache.Builder tempBuilder = rxCacheBuilder;
                    tempBuilder
                            .cacheKey(Utils.checkNotNull(cacheKey, "cachekey is null"))
                            .cacheTime(cacheTime);
                    return tempBuilder;
                } else {
                    final RxCache.Builder builder = EasyHttp.getRxCache().newBuilder();
                    builder.diskConverter(diskConverter)
                            .cacheKey(Utils.checkNotNull(cacheKey, "cachekey is null"))
                            .cacheTime(cacheTime);

                    return builder;
                }


        }
        return rxCacheBuilder;
    }


    protected R build() {
        final RxCache.Builder rxCacheBuilder = generateRxCache();
        final OkHttpClient.Builder okHttpClientBuilder = generateOkClient();
        if (cacheMode == CacheMode.DEFAULT) {
            okHttpClientBuilder.cache(cache);
        }
        final Retrofit.Builder retrofitBuilder = generateRetrofit();

        okHttpClient = okHttpClientBuilder.build();
        retrofit = retrofitBuilder.build();
        rxCache = rxCacheBuilder.build();
        apiManager = retrofit.create(ApiService.class);
        return (R) this;
    }


    protected abstract Observable<ResponseBody> generateRequest();


}
